﻿#ifndef QLIB_HEADER_LINE
#define QLIB_HEADER_LINE

#include "qlib/core/array.h"
#include "qlib/core/error.h"
#include "qlib/core/vec2.h"
#include "qlib/geo/curve.h"
#include "qlib/geo/polygon.h"
#include "qlib/layout/property.h"
#include "qlib/layout/repetition.h"

#include <algorithm>
#include <cfloat>
#include <cstdint>
#include <memory>
#include <vector>

namespace qlib {

enum struct GeoPartType;

enum PathType {
  Spine,      // 仅骨架线
  WaveGuide,  // 共面波导
  One,        //有宽度的线
  Multi
};

class Path;
using PathPtrArray = std::vector<std::shared_ptr<Path>>;
using PathPtrArrayMap = std::unordered_map<Tag, PathPtrArray, Tag::Hash>;

// 0: 中心， 1: 顶点， 2: 边， 3: 边中点，4: box顶点, 5: box边, 6: box边中心
using PathParts = std::tuple<Vec2d, Vec2dArray, std::vector<Vec2dArray>, Vec2dArray, Vec2dArray, std::vector<Vec2dArray>, Vec2dArray>;

/**
 * @brief 灵活的路径
 */
class QLIB_API Path {
 public:
  Path();

  Path(const Path& oth);
  Path& operator=(const Path& other);

  using s_ptr = std::shared_ptr<Path>;
  template <typename... Args>
  static s_ptr create(Args... args) {
    return std::make_shared<Path>(args...);
  }
  // template <>
  static s_ptr create() {
    return std::make_shared<Path>();
  }
  static s_ptr create(const Path& value) { return std::make_shared<Path>(value); }

  /**
   * @brief 拷贝
   * 
   * @return s_ptr 
   */
  s_ptr copy() const;

  void init(const Vec2d initial_position, double width, double offset, double tolerance, uint32_t layer, uint32_t datatype);
  void init(const Vec2d initial_position, const double* width, const double* offset, double tolerance, uint32_t layer, uint32_t datatype);
  void init(const Vec2d initial_position, uint64_t num_elements_, double width, double separation, double tolerance, uint32_t layer,
            uint32_t datatype);
  void init(const Vec2d initial_position, uint64_t num_elements_, const double* width, const double* offset, double tolerance, uint32_t layer,
            uint32_t datatype);

  void init_spine(Vec2dArray points, uint32_t layer = 0, uint32_t datatype = 0);
  void init_waveguide(Vec2dArray points, double line_width, double center_width, double radius, double tolerance, uint32_t layer = 0,
                      uint32_t datatype = 0);

  void apply_repetition(PathPtrArray& result);
  void arc(Vec2d radius, double initial_angle, double final_angle, double rotation, const double* width, const double* offset);

  void bezier(const Vec2dArray& point_array, const double* width, const double* offset, bool relative);

  uint64_t commands(const CurveInstruction* items, uint64_t count);

  void cubic(const Vec2dArray& point_array, const double* width, const double* offset, bool relative);
  void cubic_smooth(const Vec2dArray& point_array, const double* width, const double* offset, bool relative);

  void horizontal(double coord_x, const double* width, const double* offset, bool relative);
  void horizontal(const std::vector<double>& coord_x, const double* width, const double* offset, bool relative);

  void interpolation(const Vec2dArray& point_array, double* angles, bool* angle_constraints, Vec2d* tension, double initial_curl, double final_curl,
                     bool cycle, const double* width, const double* offset, bool relative);
  void mirror(const Vec2d p0, const Vec2d p1);
  // void offsets(Vec2dArray& res) const;

  void parametric(ParametricVec2 curve_function, void* data, const double* width, const double* offset, bool relative);
  void path_spines(Vec2dArray& res);

  void quadratic(const Vec2dArray& point_array, const double* width, const double* offset, bool relative);
  void quadratic_smooth(Vec2d end_point, const double* width, const double* offset, bool relative);
  void quadratic_smooth(const Vec2dArray& point_array, const double* width, const double* offset, bool relative);

  void rotate(double angle, const Vec2d center);

  void scale(double scale, const Vec2d center);

  void segment(Vec2d end_point, const double* width, const double* offset, bool relative);
  void segment(const Vec2dArray& point_array, const double* width, const double* offset, bool relative);

  // void spine(Vec2dArray& res);
  ErrorCode to_polygons(bool filter, Tag tag, PolygonPtrArray& res);
  Polygon::s_ptr to_polygon();

  void translate(double dx, double dy);

  void turn(double radius, double angle, const double* width, const double* offset);
  void vertical(double coord_y, const double* width, const double* offset, bool relative);
  void vertical(const std::vector<double>& coord_y, const double* width, const double* offset, bool relative);

  void widths(std::vector<double>& res);

  void fill_offsets_and_widths(const double* width, const double* offset);

  void remove_overlapping_points();

  ErrorCode element_center(const LineElement* el, Array<Vec2d>& result);

  void transform(double magnification, bool x_reflection, double rotation, const Vec2d origin);

  void widthToElement();

  Vec2dArray bounding_box_points() { return box_to_points(bounding_box()); };
  Vec2dArray bounding_box();

  PathParts get_parts();
  Vec2dArray query_parts(GeoPartType type, uint32_t id);

  // 渲染
  Vec2dArray render_aabb();
  Vec2dArray render_poly();
  Vec2dArray render_absorp();

  double getLineWidth();
  void setLineWidth(double width);

  double getCenterWidth();
  void setCenterWidth(double width);

  double getLineRadius();
  void setLineRadius(double radius);

  void updateWidthAndRadius();

  uint32_t getLayer();
  void setLayer(uint32_t layer);

  // 判断类型
  void updateType();

  // 更新Elements
  void updateElements();

  // 修改线宽、中心线宽、转弯半径后使用
  void update();

  // 共面波导
  void coplanarWaveguide(double line_width, double center_line_width, double bend_radius, uint32_t layer_ = 0);

  // 根据属性判断类型，仅在绘制线调用
  void drawType();

  std::string getNetName();
  void setNetName(std::string net_name);

 public:
  PathType type;

  Curve spine;

  Vec2dArray box;
  std::vector<Polygon::s_ptr> polys;

  double width{0};         // db读取,线宽
  double center_width{0};  // 共面波导中心线宽
  double radius{0};        // 转弯半径

  EndType end_type;
  Vec2d end_extensions;

  uint32_t layer = 0;
  uint32_t datatype = 0;
  std::vector<LineElement> elements;

  bool simple_path;
  bool scale_width;

  RaithData m_raith_data;

  PropertyArray properties;
  Repetition repetition;

  void* owner;

 private:
  std::string m_net_name = "";  //网络名称
};

// class SpinePath : public Path {
//  public:
//   SpinePath();

//   const uint64_t element_number{1};
// };

// class WaveGuidePath : public Path {
//  public:
//   WaveGuidePath();

//   const uint64_t element_number{2};
//   double inner_width;
//   double outer_width;
// };

}  // namespace qlib

#endif